package MobileMe;

use warnings;
use strict;
use LWP::UserAgent;
=head1 NAME

MobileMe - The API to access Apple's MobileMe!

=head1 VERSION

Version 0.01

=cut

our $VERSION = '0.01';


=head1 SYNOPSIS

A Perl wrapper for Apple's MobileMe Services.

    use MobileMe;

    my $mm = MobileMe->new(username,password);
    print $mm->GetLocation(deviceid,deviceversion);
    ...

=head1 FUNCTIONS

=head2 new($username,$password)

This function creates the initial connection to MobileMe, and authenticates using the provided username and password

=cut

sub new ($$$)
{
    my $class = shift;
    my $self = {
        _username => shift,
        _password => shift,
        ua => LWP::UserAgent->new,
        _isckey => '',
        _lsckey => '',
    };
    
    bless $self,$class;
    
    $self->{ua}->agent("Mozilla/5.0 (Macintosh; U; Intel Mac OS X 10.5; en-US; rv:1.9.0.10) Gecko/2009042315 Firefox/3.0.10");
    $self->{ua}->cookie_jar( {} );
    $self->mm_auth();
    return $self;
}

sub debug($$)
{
    my $self = shift;
    my $v = shift;
    print 'DEBUG: ' . $v . "";
}

sub mm_auth()
{
    my $self = shift;
    my $uri = 'https://auth.apple.com/authenticate';
    my $content_type = 'application/x-www-form-urlencoded';
    my $content = '';
    $content .= 'returnURL=aHR0cHM6Ly9zZWN1cmUubWUuY29tL3dvL1dlYk9iamVjdHMvRG9ja1N0YXR1cy53b2Evd2EvdHJhbXBvbGluZT9kZXN0aW5hdGlvblVybD0vYWNjb3VudA==';
    $content .= '&' . 'service=DockStatus';
    $content .= '&' . 'realm=primary-me';
    $content .= '&' . 'cancelURL=http://www.me.com/mail';
    $content .= '&' . 'formID=loginForm';
    $content .= '&' . 'username=' . $self->{_username};
    $content .= '&' . 'password=' . $self->{_password};
    
    $self->debug("Sending auth request to auth.apple.com...\n");
    my $res = $self->mm_request('POST',$uri,$content_type,$content);
    die "Error at $uri\nThe server did not respond with a valid new location - aborting.\n" . $res->as_string unless $res->code == '302' && length($res->header('Location')) > 0;
    
    $self->debug("Sending auth request to secure.me.com...\n");
    $uri = $res->header('Location');
    $res = $self->mm_request('GET',$uri,'','');

    my $body = ($res->header('Content-Encoding') && $res->header('Content-Encoding') eq 'gzip')? Compress::Zlib::memGunzip($res->content): $res->content ;
    die "Error at $uri\nThe server did not respond with a valid new location - aborting.\n" . $res->as_string unless $res->is_success;

    
}

sub mm_request($$$$$)
{
    my $self = shift;
    my ($method,$uri,$content_type,$content) = @_;
    my $r = HTTP::Request->new($method);
    $r->uri($uri);
    $r->content_type($content_type);
    $r->content($content);
    $r->header(Accept => "text/html,application/xhtml+xml,application/xml;q=0.9,*/*;q=0.8");
    $r->header(Accept_Language => "en-us,en;q=0.5");
    $r->header(Accept_Encoding => "gzip,deflate");
    $r->header(Accept_Charset => "ISO-8859-1,utf-8;q=0.7,*;q=0.7");
    $r->header(Referer => "https://secure.me.com/account/");
    my $res = $self->{ua}->request($r);
    $self->{ua}->cookie_jar->scan(sub {
        my($version,$key,$val,$path,$domain,$port,$path_spec,$secure,$expires,$discard,$rest) = @_;
            if ($domain eq 'secure.me.com')
            {
                if ($key eq 'isc-secure.me.com') {
                    $self->{_isckey} = $val;
                }
                if ($key eq 'lsc-DockStatus') {
                    $self->{_lsckey} = $val;
                }
            }
        });
    return $res;
}
sub mm_ajaxrequest($$$$$)
{
    my $self = shift;
    my ($method,$uri,$content_type,$content) = @_;
    my $r = HTTP::Request->new($method);
    $r->uri($uri);
    $r->content_type($content_type);
    $r->content($content);
    $r->header(Accept => "text/html,application/xhtml+xml,application/xml;q=0.9,*/*;q=0.8");
    $r->header(Accept_Language => "en-us,en;q=0.5");
    $r->header(Accept_Encoding => "gzip,deflate");
    $r->header(Accept_Charset => "ISO-8859-1,utf-8;q=0.7,*;q=0.7");
    $r->header(Referer => "https://secure.me.com/account/");
    $r->header(X_Requested_With => 'XMLHttpRequest');
    $r->header(X_Prototype_Version => '1.6.0.3');
    $r->header(X_Mobileme_Version => '1.0');
    $r->header(X_Mobileme_Isc => $self->{_isckey});
    $r->header(Referer => "https://secure.me.com/account/");
    my $res = $self->{ua}->request($r);
    $self->{ua}->cookie_jar->scan(sub {
        my($version,$key,$val,$path,$domain,$port,$path_spec,$secure,$expires,$discard,$rest) = @_;
            if ($domain eq 'secure.me.com')
            {
                if ($key eq 'isc-secure.me.com') {
                    $self->{_isckey} = $val;
                }
                if ($key eq 'lsc-DockStatus') {
                    $self->{_lsckey} = $val;
                }
            }
        });
    return $res;
}

=head2 DoLocate($deviceId,$deviceVersion)

This function starts a location update request, to get the most current location.

=cut

sub DoLocate($$$)
{
    my $self = shift;
    my $deviceId = shift;
    my $deviceVersion = shift;
    
    # fancy way of putting last char first
    $deviceId = chop($deviceId) . $deviceId;
    
    $self->debug("Sending location request to secure.me.com...\n");

    my $uri = 'https://secure.me.com/wo/WebObjects/DeviceMgmt.woa/wa/LocateAction/locate';
    my $content = 'postBody={"deviceId": "' . $deviceId . '", "deviceOsVersion": "' . $deviceVersion . '"}';
    my $content_type = 'application/json; charset=UTF-8';
    my $res = $self->mm_ajaxrequest('POST',$uri,$content_type,$content);
    my $body = ($res->header('Content-Encoding') && $res->header('Content-Encoding') eq 'gzip')? Compress::Zlib::memGunzip($res->content): $res->content ;
    die "Error at $uri\nThe server did not respond with a valid new location - aborting.\n" . $res->as_string unless $res->is_success;
    return $body . "\n";
}
    
=head2 GetLocationStatus($deviceId,$deviceVersion)

This function gets the last reported location.

=cut

sub GetLocationStatus($$$)
{
    my $self = shift;
    my $deviceId = shift;
    my $deviceVersion = shift;
    
    # fancy way of putting last char first
    $deviceId = chop($deviceId) . $deviceId;
    
    $self->debug("Sending location request to secure.me.com...\n");

    my $uri = 'https://secure.me.com/wo/WebObjects/DeviceMgmt.woa/wa/LocateAction/locateStatus';
    my $content = 'postBody={"deviceId": "' . $deviceId . '", "deviceOsVersion": "' . $deviceVersion . '"}';
    my $content_type = 'application/json; charset=UTF-8';
    my $res = $self->mm_ajaxrequest('POST',$uri,$content_type,$content);
    my $body = ($res->header('Content-Encoding') && $res->header('Content-Encoding') eq 'gzip')? Compress::Zlib::memGunzip($res->content): $res->content ;
    die "Error at $uri\nThe server did not respond with a valid new location - aborting.\n" . $res->as_string unless $res->is_success;
    return $body . "\n";
}

=head1 AUTHOR

Lokkju, C<< <lokkju> >>

=head1 BUGS

Please report any bugs or feature requests through
the web interface at L<http://code.google.com/p/mobileme/issues/list>.  I will be notified, and then you'll
automatically be notified of progress on your bug as I make changes.



=head1 SUPPORT

You can find documentation for this module with the perldoc command.

    perldoc MobileMe


You can also look for information at:

=over 4

=item * The Google Code MobileMe API project site

L<http://mobileme.googlecode.com/>

=back


=head1 ACKNOWLEDGEMENTS


=head1 COPYRIGHT & LICENSE

Copyright 2009 Lokkju, all rights reserved.

MobileMe (TM)

This program is free software; you can redistribute it and/or modify it
under the same terms as Perl itself.


=cut

1; # End of MobileMe
